fix(a11y): WCAG 1.4.1 — add non-color visual cues to status indicators#39233
fix(a11y): WCAG 1.4.1 — add non-color visual cues to status indicators#39233Aitema-gmbh wants to merge 3 commits intoapache:masterfrom
Conversation
…essages (WCAG 1.4.1) - Replace generic icons in AlertStatusIcon with distinct shapes per status: Success=CheckCircleOutlined, Error=CloseCircleOutlined, Working=LoadingOutlined, Noop=ClockCircleOutlined, Grace=ExclamationCircleOutlined - Add "Error:" text prefix to ChartErrorMessage and ImportModal ErrorAlert - Add aria-label with scheme name and role="option" to ColorSchemeLabel swatches - Mark individual color swatches as aria-hidden for cleaner screen reader output - Add tests verifying distinct icon rendering per status, icon presence in ErrorAlert, and aria-label on ColorSchemeLabel
Code Review Agent Run #b821ebActionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
| theme, | ||
| )} | ||
| spin={isWorkingState} | ||
| aria-hidden="true" |
There was a problem hiding this comment.
Suggestion: The icon is explicitly hidden from assistive technologies, but this component renders only the icon in the table cell, so screen reader users get no status information at all. Replace the hidden attribute with an accessible label so the status can be announced. [possible bug]
Severity Level: Critical 🚨
- ❌ Alert list status column unreadable for screen reader users.
- ❌ Execution log state column inaccessible to assistive technologies.
- ⚠️ Violates WCAG 2.1 use-of-color requirements.| aria-hidden="true" | |
| aria-label={lastStateConfig.label} |
Steps of Reproduction ✅
1. Navigate to the alerts list at `/alert/list/`, which is wired to `AlertReportList` via
the route configuration in `superset-frontend/src/views/routes.tsx:16-24` (path
`/alert/list/` uses `Component: AlertReportList`).
2. In `AlertReportList`, observe the first table column definition at
`superset-frontend/src/pages/AlertReportList/index.tsx:138-55`, where the `Cell` renderer
returns only `<AlertStatusIcon state={lastState} isReportEnabled={isReportEnabled} />`
with no additional text in that cell.
3. Open `superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx` and note
that the component computes a human-readable status label in `lastStateConfig.label` for
each state (lines 60-89) and passes it only to the tooltip (`<Tooltip
title={lastStateConfig.label} ...>` at lines 99-100), while the rendered icon element at
lines 107-116 uses `aria-hidden="true"` on the `<Icon>` (line 115) and there is no other
text content in the `<span>` wrapper.
4. With a screen reader enabled, navigate the alerts table row-by-row; when focus/virtual
cursor reaches the status column (the cell whose contents are just `AlertStatusIcon`), no
status text (e.g., "Report sent", "Alert failed") is announced because the only element
conveying status is the icon with `aria-hidden="true"`, and the tooltip title is not
exposed as cell text. The same pattern appears for the execution log state column in
`superset-frontend/src/pages/ExecutionLogList/index.tsx:78-29`, which also renders only
`<AlertStatusIcon ... />` in the `State` column.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx
**Line:** 115:115
**Comment:**
*Possible Bug: The icon is explicitly hidden from assistive technologies, but this component renders only the icon in the table cell, so screen reader users get no status information at all. Replace the hidden attribute with an accessible label so the status can be announced.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.| */ | ||
|
|
||
| import { ClientErrorObject, SupersetError } from '@superset-ui/core'; | ||
| import { t } from '@apache-superset/core'; |
There was a problem hiding this comment.
Suggestion: The translation helper is imported from the package root, but @apache-superset/core only exposes translation as a namespace export. Importing t directly from the root will resolve to an undefined/non-exported symbol and can fail at build/runtime. Import t from the translation submodule instead. [type error]
Severity Level: Critical 🚨
- ❌ TS build fails: no 't' in core index.ts.
- ❌ ChartErrorMessage cannot compile where it is imported.
- ⚠️ Chart error UI unavailable across chart views.| import { t } from '@apache-superset/core'; | |
| import { t } from '@apache-superset/core/translation'; |
Steps of Reproduction ✅
1. Build or test the frontend so TypeScript compiles `superset-frontend` (e.g. running the
standard frontend build/test task that includes
`src/components/Chart/ChartErrorMessage.tsx`).
2. During module resolution, TypeScript loads `@apache-superset/core` from
`packages/superset-core/src/index.ts:19-31`, which only exports namespace objects like
`common`, `authentication`, `translation`, etc., and does not export a named `t` symbol.
3. The compiler then processes `src/components/Chart/ChartErrorMessage.tsx:21`, which
contains `import { t } from '@apache-superset/core';`, and checks this against the exports
from `packages/superset-core/src/index.ts`, finding no `t` export and raising a
compile-time error (`Module '@apache-superset/core' has no exported member 't'`).
4. Note that the correct `t` export is defined in
`packages/superset-core/src/translation/TranslatorSingleton.ts:66-80` and re-exported via
`packages/superset-core/src/translation/index.ts:20-22`, which is what existing call sites
use through `import { t } from '@apache-superset/core/translation';` (see multiple usages
in `superset-frontend/plugins/...`), confirming that the root import in
`ChartErrorMessage.tsx` is inconsistent and incorrect.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** superset-frontend/src/components/Chart/ChartErrorMessage.tsx
**Line:** 21:21
**Comment:**
*Type Error: The translation helper is imported from the package root, but `@apache-superset/core` only exposes `translation` as a namespace export. Importing `t` directly from the root will resolve to an undefined/non-exported symbol and can fail at build/runtime. Import `t` from the translation submodule instead.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.| `} | ||
| data-test={id} | ||
| aria-label={t('Color scheme: %s', label)} | ||
| role="option" |
There was a problem hiding this comment.
Suggestion: Adding an explicit role="option" on this inner <span> creates invalid ARIA semantics because this component is also rendered as selected-value content, outside a listbox context, and can also nest inside an actual option element. This causes accessibility tree inconsistencies and can break screen-reader navigation. Remove the explicit option role and keep only the accessible label text. [logic error]
Severity Level: Major ⚠️
- ⚠️ Color-scheme dropdown options expose nested role="option" elements.
- ⚠️ Selected color-scheme label has role="option" outside listbox.
- ⚠️ Screen-reader navigation for color schemes becomes semantically inconsistent.
- ⚠️ Explore and dashboard color-scheme selectors risk ARIA non-compliance.| role="option" |
Steps of Reproduction ✅
1. Open the Explore view for any chart and locate the "Color scheme" control, which is
wired up from `color_scheme` in `superset-frontend/src/explore/controls.tsx:11-19` (type
`'ColorSchemeControl'`) to the `ColorSchemeControl` React component in
`superset-frontend/src/explore/components/controls/ColorSchemeControl/index.tsx:12-35`.
2. In `ColorSchemeControl`, note that the color-scheme options passed to the `Select`
component are built in `index.tsx:79-103`, where each option's `label` is a
`<ColorSchemeLabel>` element (`ColorSchemeLabel` imported at `index.tsx:39`).
3. Inspect `ColorSchemeLabel` implementation in
`superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.tsx:81-101`:
the outer `<span className="color-scheme-option">` used as the option label includes both
`aria-label={t('Color scheme: %s', label)}` (line 99) and `role="option"` (line 100). When
the `Select` (rendered in `ColorSchemeControl/index.tsx:296-32`) opens its dropdown, it
renders each option with its own listbox semantics (Set by `Select` from
`@superset-ui/core/components`), and the `ColorSchemeLabel` span becomes nested inside
that option container.
4. Run the frontend, open the "Color scheme" dropdown in Explore or in the dashboard-level
`ColorSchemeSelect`
(`superset-frontend/src/dashboard/components/ColorSchemeSelect.tsx:111-119` uses the same
`ColorSchemeLabel` as option labels and `ColorSchemeSelect.tsx:165-30` renders a `Select`
with those options). Using a screen reader or browser accessibility inspector, observe
that each option contains an inner element with `role="option"` from `ColorSchemeLabel`,
resulting in an option element inside another option, and that the same `ColorSchemeLabel`
span (still with `role="option"`) is also used as the selected-value content when the
dropdown is closed, i.e., outside any `listbox` context. This produces invalid ARIA
semantics in both the dropdown and the collapsed control, confirming the issue described
in the suggestion.Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.tsx
**Line:** 100:100
**Comment:**
*Logic Error: Adding an explicit `role="option"` on this inner `<span>` creates invalid ARIA semantics because this component is also rendered as selected-value content, outside a listbox context, and can also nest inside an actual option element. This causes accessibility tree inconsistencies and can break screen-reader navigation. Remove the explicit option role and keep only the accessible label text.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Code Review Agent Run #dbea3dActionable Suggestions - 0Additional Suggestions - 2
Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
|
Thanks for not hard coding the 700 font weight value. I see we're just leaning on the strong tags, which is great.Again, re-blind about feedback would be helpful. |
There was a problem hiding this comment.
Pull request overview
This PR targets WCAG 2.1 criterion 1.4.1 (“Use of Color”) by adding additional non-color cues (icons/labels/patterns) to help users distinguish statuses and UI states without relying on color alone.
Changes:
- Updated alert/report status iconography and added a spin indicator for “working” state.
- Improved ColorScheme option accessibility by hiding decorative swatches from assistive tech and adding an aria-label; added unit tests.
- Adjusted error message presentation/wording and added tests asserting presence of non-color icon cues.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| superset-frontend/src/features/alerts/components/AlertStatusIcon.tsx | Swap status icons and add spinning animation for “working” state. |
| superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.tsx | Add aria-label to scheme label container and aria-hidden to decorative swatches. |
| superset-frontend/src/explore/components/controls/ColorSchemeControl/ColorSchemeLabel.test.tsx | Add tests for the new aria-label and aria-hidden behavior. |
| superset-frontend/src/components/ImportModal/ErrorAlert.tsx | Prefix import error alert message with a bold “Error:” label. |
| superset-frontend/src/components/ErrorMessage/ErrorAlert.test.tsx | Add tests to ensure icons render as a non-color cue (regular + compact modes). |
| superset-frontend/src/components/Chart/ChartErrorMessage.tsx | Update default chart error title to include an “Error:” prefix (localized). |
| @@ -116,6 +111,7 @@ export default function AlertStatusIcon({ | |||
| isReportEnabled, | |||
| theme, | |||
| )} | |||
| spin={isWorkingState} | |||
| /> | |||
There was a problem hiding this comment.
The icon’s accessible name currently comes from the icon filename (e.g. “check-circle”, “clock-circle”), which doesn’t convey the actual alert/report status to screen reader users. Since this component already has a localized lastStateConfig.label, consider wiring that into the rendered icon (e.g. via aria-label / title) so the status is announced meaningfully (and not just via a hover tooltip).
| setup(); | ||
| const option = screen.getByRole('option', { | ||
| name: 'Color scheme: Superset Colors', | ||
| }); | ||
| expect(option).toBeInTheDocument(); |
There was a problem hiding this comment.
ColorSchemeLabel is rendered standalone in this test, but the component doesn’t render any element with role="option" (it’s a plain span), so getByRole('option', { name: ... }) will fail. Consider asserting the aria-label directly (e.g. via getByLabelText / querying [aria-label=...]) or render it within the Select option markup if you specifically want to test the option’s accessible name.
| setup(); | |
| const option = screen.getByRole('option', { | |
| name: 'Color scheme: Superset Colors', | |
| }); | |
| expect(option).toBeInTheDocument(); | |
| const { container } = setup(); | |
| const swatchContainer = container.querySelector( | |
| '[aria-label="Color scheme: Superset Colors"]', | |
| ); | |
| expect(swatchContainer).toBeInTheDocument(); |
…lid role
The previous test queried for `getByRole('option')`, but the component
no longer renders that role (removed in ac86959 because role="option"
outside an actual listbox is invalid ARIA). Replaced the lookup with
`getByLabelText` which directly verifies the accessible name without
making structural assumptions about the rendering element.
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Code Review Agent Run #846b50Actionable Suggestions - 0Review Details
Bito Usage GuideCommands Type the following command in the pull request comment and save the comment.
Refer to the documentation for additional commands. Configuration This repository uses Documentation & Help |
SUMMARY
Implements WCAG 2.1 criterion 1.4.1 (Use of Color, Level A).
font-weight: 700as secondary visual cue for active tabsTESTING INSTRUCTIONS
ADDITIONAL INFORMATION
Part of a series of 16 individual WCAG 2.1 accessibility PRs. See also fix(a11y): Accessibility improvements for WCAG 2.1 Level A compliance #38342.